package net.slowpoison.loveclock.client;

import java.util.ArrayList;
import java.util.Date;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import com.google.gwt.user.datepicker.client.DateBox;
//import com.google.gwt.user.datepicker.client.DatePicker;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class LoveClock implements EntryPoint {
	/**
	 * The message displayed to the user when the server cannot be reached or
	 * returns an error.
	 */
	
	/**
	 * Create a remote service proxy to talk to the server-side Greeting service.
	 */
	private static final int TIME_UPDATE_MSECS = 1000;
	private static final int ANIM_UPDATE_MSECS = 100;
	private static final int MAX_DOTS = 600;

	private static Dot dotPool[] = new Dot[MAX_DOTS];
	private static boolean dotsUsed[] = new boolean[MAX_DOTS];
	private static ArrayList<Dot> animateDotPool;
	
	private static RootPanel rootPanel;
	
	private long timeDiff;
	private String timeDiffStr;
	private Ticker ticker;
	private int sync = 0;
	private Button animButton = new Button("Stop Animation");
	private boolean animating = true;
	private DateBox datePicker = new DateBox();
	private Timer tickUpdateTimer = new Timer() {
		@Override
		public void run() {
			sync += TIME_UPDATE_MSECS;
			updateTimeDiffStr(TIME_UPDATE_MSECS);
			if (animating) {
				ticker.set(timeDiffStr);
			}
			
			// re-sync time every 60 secs
			if (sync >= 60*1000) {
				calcTimeDiffStr(datePicker.getValue());
			}
		}
	};
			
	private Timer animationTimer = new Timer() {
		@Override
		public void run() {
			for(int i=0; i < animateDotPool.size(); ++i) {
				Dot d = animateDotPool.get(i);
				boolean anim = d.animate();
				if (!anim) {
					animateDotPool.remove(i);
					finalReturnDot(d);
					--i;	// account for the lost dot
				}
			}
		}
	};

	
	/**
	 * This is the entry point method.
	 */
	@SuppressWarnings("deprecation")
	public void onModuleLoad() {
		rootPanel = RootPanel.get("rootPanel");
		Dot.init();
		initDotPool();
		
		
		HorizontalPanel hp = new HorizontalPanel();
		Label l = new Label(" Pick a date to start a new countdown: ");
		hp.add(l);
		hp.setCellVerticalAlignment(l, HasVerticalAlignment.ALIGN_MIDDLE);
		hp.add(datePicker);
		hp.add(new HTML("&nbsp;&nbsp;"));
		hp.add(animButton);
		hp.getElement().getStyle().setProperty("margin", "auto");
		rootPanel.add(hp);
		
		datePicker.setValue(new Date(2011 - 1900, 4, 10, 9, 0)); // Start with IO 2011 date
		datePicker.addValueChangeHandler(new ValueChangeHandler<Date>() {
			@Override
			public void onValueChange(ValueChangeEvent<Date> event) {
				calcTimeDiffStr(event.getValue());
				ticker.set(timeDiffStr);
			}
		});
		animButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				startStopAnimation();
			}
			
		});
		
		calcTimeDiffStr(datePicker.getValue());
		ticker = new Ticker(timeDiffStr, (Window.getClientWidth() - Ticker.getWidth()) >> 1 ,
						datePicker.getAbsoluteTop() + 200);
		animationTimer.scheduleRepeating(ANIM_UPDATE_MSECS);
		tickUpdateTimer.scheduleRepeating(TIME_UPDATE_MSECS);
	}
	
	private void startStopAnimation() {
		if (animating) {
			animButton.setText("Start Animation");
			animating = false;
//			tickUpdateTimer.cancel();
		} else {
			animButton.setText("Stop Animation");
			animating = true;
//			tickUpdateTimer.scheduleRepeating(1000);
		}
			
//		tickUpdateTimer.schedule(1000);
	}
	
	protected int getHeartsUsed() {
		int count = 0;
		for (int i=0; i < MAX_DOTS; ++i) {
			if (dotsUsed[i] && dotPool[i].getType() != Dot.DotType.BG)
				++count;
		}
		return count;
	}

	protected int getHeartsUnused() {
		int count = 0;
		for (int i=0; i < MAX_DOTS; ++i) {
			if (!dotsUsed[i] && dotPool[i].getType() != Dot.DotType.BG)
				++count;
		}
		return count;
	}
	
	protected int getGraysUsed() {
		int count = 0;
		for (int i=0; i < MAX_DOTS; ++i) {
			if (dotsUsed[i] && dotPool[i].getType() == Dot.DotType.BG)
				++count;
		}
		return count;
	}
	
	protected int getGraysUnused() {
		int count = 0;
		for (int i=0; i < MAX_DOTS; ++i) {
			if (!dotsUsed[i] && dotPool[i].getType() == Dot.DotType.BG)
				++count;
		}
		return count;
	}

	private static void initDotPool() {
		Dot dot;
		for (int i=0; i < MAX_DOTS; ++i) {
			dot = new Dot(Dot.DotType.BG, i);
			dotPool[i] = dot;
			// make the dot remember where it is
			dotsUsed[i] = false;
			// add the dot
			rootPanel.add(dot.getImage());
		}
		
		animateDotPool = new ArrayList<Dot>();
	}

	public static Dot borrowDot(Dot.DotType type, int x, int y) {
		Dot dot;
		for (int i=0; i < MAX_DOTS; ++i) {
			if (!dotsUsed[i]) {
				dotsUsed[i] = true;
				dot = dotPool[i];
				dot.set(type, true, x, y);
				return dot;
			}
		}
		return null;
	}
	
	public static void returnDot(Dot dot) {
		if (dot.getType() != Dot.DotType.BG) {
			animateDotPool.add(dot);
		} else {
			dot.reset();
			dotsUsed[dot.getIndex()] = false;
		}
	}

	public static void finalReturnDot(Dot dot) {
		dot.reset();
		dotsUsed[dot.getIndex()] = false;
	}
	
	private void calcTimeDiffStr(Date future) {
    		Date now = new Date();
    		timeDiff = future.getTime() - now.getTime();
    		timeDiffStr = getTimeStr(timeDiff);
    }
	
	protected void updateTimeDiffStr(int msec) {
		timeDiff -= 1000;
		timeDiffStr = getTimeStr(timeDiff);
	}
	
	protected String getTimeStr(long time) {
		long d,h,m,s;
		if (time < 0)
			time = -time;
		d = time / (24 * 60 * 60 * 1000);
		time -= d * 24 * 60 * 60 * 1000;
		String dstr = String.valueOf(d);
		if (dstr.length() == 1)
			dstr = "00" + dstr;
		else if (dstr.length() == 2)
			dstr = "0" + dstr;
		
		h = time / (60 * 60 * 1000);
		time -= h * 60 * 60 * 1000;
		String hstr = String.valueOf(h);
		if (hstr.length() == 1)
			hstr = "0" + hstr;
		
		m = time / (60 * 1000);
		time -= m * (60 * 1000);
		String mstr = String.valueOf(m);
		if (mstr.length() == 1)
			mstr = "0" + mstr;
		
		s = time / (1000);
		String sstr = String.valueOf(s);
		if (sstr.length() == 1)
			sstr = "0" + sstr;
		
		return dstr + ":" + hstr + ":" + mstr + ":" + sstr;
	}


	public static int getScreenWidth() {
		return Window.getClientWidth();
	}
	
	public static int getScreenHeight() {
		return Window.getClientHeight();
	}

	public static void add(Image image) {
		rootPanel.add(image);
	}
}